Simulink代码生成:04 代码复用 Reuse

您所在的位置:网站首页 simulink slxc文件 Simulink代码生成:04 代码复用 Reuse

Simulink代码生成:04 代码复用 Reuse

#Simulink代码生成:04 代码复用 Reuse| 来源: 网络整理| 查看: 265

在建模的时候,经常会遇到这种情况:slx 模型不同的地方需要实现同一个【功能】。而我们希望在代码生成过程中,它只生成一份代码。这又涉及到只出现在同一个slx文件的【功能】代码复用,还可能出现在不同slx文件的【功能】代码复用。

首先,这个【功能】本身在 Simulink 里可能被实现为:

模型引用 Model Reference子系统 Subsystem库 Library其它

相关模型和代码下载:PCG 代码生成入门案例 - File Exchange - MATLAB Central (mathworks.com)

0 Reuse 和 Reentrant

但是考虑到 Simulink 模型的特点,以及它的代码生成配置方式,代码复用还是可以稍作区别。

Reusable

以 Top Model 作为例子,把这个 slx 模型配置成了 Reusable function 。

打开 Reusable.slx

对应的代码,step 函数原型里,第一个形参就是模型数据以及状态量,所以就算多次调用,每一次调用之间的状态不影响,互相独立。

生成代码这里形参 _M 保存的是 Unit Delay 模块状态量。

类似于右图的 sensor_read() 函数,main 函数每一次调用它都有自己对应的数据单独处理和保存,互不影响。

Simulink 说的 Reentrant,我认为就是配置项里对应的 Reusable 的概念。

NonReusable

本来 slx 模型默认生成的是 void-void 函数。

这里为了进一步对比NonReusable,把它配置为【函数原型配置成形参且传指针】。

打开Reusable2

可以看到,NonReusable 即使有形参也只涉及到输入与输出,中间变量读写的是全局变量 Reusable2_DW,如果在模型里被多次调用的话,他们的状态量是共享的。

所以同样的输入可能会得到不同的输出,而且调用的顺序就很重要。在 Simulink 里做配置的时候,这一类都是归类为 Non - Reusable Code

1 模型引用模型引用也是单独的 .slx文件,对于代码生成来说,这里提模型引用 Model Reference 主要是强调它是作为Top Model 的一部分,一起生成代码的。

首先模型引用是可以被同一个模型调用多次的。

刚刚提到的 Reusable 概念,在模型引用这里就是按照下图把设置为 Multiple

可以看到这个属性是分类在 Model Referencing 下面的,所以其实也就是“当它作为一个模型被引用的时候”的控制量。打开被引用模型 ex_arg_code_ref_01

它只会生成同一份代码,就算跨模型调用,不同层级调用也是如此。因为它本身就指向同一个文件,也没有复制粘贴为多个模型元素。

经过配置后,还支持支持参数化调用。详细的过程在专门介绍模型引用再提,就不重复再放这里了。

打开 Top Model : ex_arg_code_01生成代码

文件结构也很简单:

top Model 的主文件单独在最外面;凡是被引用模型,不管在那个层级被引用的都并列排在 ert 文件夹下。这里的 ex_arg_code_01 里面没啥东西;

模型引用一般用于比较大型的【功能组成】、或者是需要单独测试管理的功能。

如果模型引用在 Top Model 里只会被调用一次,把刚刚的 Multiple 配置为 One,得到的函数原型就不会传递状态量结构体了,而是直接访问全局变量。

打开被引用模型 ex_arg_code_ref_01,检查配置打开 Top Model:ex_arg_code_SingleInstance生成代码

2 子系统配置项

和模型引用不同,子系统需要稍微配置一下。

首先它不能是虚拟的子系统,按照 Simulink 的术语,它是 atomic 的。

Function packaging 有三种方式,这名字就很直接。其中 Auto 则表示由 Coder Generator 来决定选择后面三种的任意一种。

刚刚提过很多次,这里 Reusable 和 Nonreusable,不仅仅是说输入输出端口,还可能包括子系统要用到的数据(比如查表的表格)以及内部的状态量(如果内部有带状态的模块,比如传递函数)。

接着来看示例代码

打开模型 : ex_model_Top,生成代码Reusable 和 Nonreusable 子模型 A 内部

这四个模块其实都是一样的,只是做了不同的代码生成配置。

上面两个子系统都配置为 Reusable function

所以右图这两次计算调用的是同一段代码。这里函数为所指定的 myfun,并定义在所指定的 myfile.c 。这子系统内有个查表模块,在子系统封装界面上可以给定不同的参数。从代码看两次调用传递了不同的参数,仍然可以生成可复用的代码。

下面两个子系统都配置为 Nonreusable function

所以可以看到图上调用的是两个函数。哪怕内容一样,配置成了Nonreusable ,所以也生成了两份代码。

如果仅仅是为了子系统代码分割成专门的函数和文件的话,Reusable function 都可以实现。那,这么说来,Nonreusable function 的存在还有什么意义吗?

有时候还是有点意义的。Nonreusable function 所有的数据交换默认都是通过全局变量实现,不需要传参

这里是特意配置成了 Allow argument (Match graphical Interface),形参保持跟输入输出端口一致,没选 void-void 选项。对比上面的函数原型,Nonreusable function 依旧少了几个输入,查表要用的数据,这里就直接访问全局变量去了。

子系统跨模型复用 查看子模型B

虽然子系统 SS1, SS2 和子模型A的一模一样,但这里的子系统并没有得到跨AB模型复用,A生成一遍,B又生成一遍。

其实这也很能理解,通过复制粘贴的方式时刻人工保持两个或更多 slx 文件用到的SSx一致,是不现实的。不过这张贴图不是很合适,毕竟从名字能看出来它的模块做了不同的配置,filename 和 function name 都设置为 Auto 了。

但就算A模型的SS子系统也同样的配置Auto,也依然额外生成了自己的一份代码。

那为什么不再对比一下跨模型子系统都设置成同样的函数名和文件名会得到什么样的代码呢?

因为会报错。

所以,有没有可能实现跨模型的零碎功能代码复用呢?

有。或许有别的方法,比如,库。

3 库模块a 固定库

新建一个文件(注意不是模型文件),把刚才设计好的可重用子系统放到库中,并指定文件名以及函数名

具体过程就不描述了,直接打开 FixedLib.slx

新建一个 Simulink 模型 用到这些模块,再生成代码。

>>ex_topmdl_FixLib

可见,这个模块的代码得到了跨模型复用,生成了指定文件名myfun_fixed,放在了共享文件夹。如果留意到,可以看到这个文件夹下还有一个查表模块对应的代码,这是因为库模块里面有一个查表模块。

下图是其中一个子模型,可以看到库模块对应的代码生成了指定的函数名 myfun_fixed

当 Top Model 进行修改重新生成代码时,这些 sharedutils 的部分并不需要重新生成

我们试试把其中一个被引用模型 ex_model1_fixed 修改了下,重新生成代码如下。不过这个代码具体不重要,只是后面再看下其它文件夹的修改时间戳。

从下图的时间戳可见,只是被修改的模型 ex_model1_fixed 文件夹更新了修改时间。而其它部分比如 sharedutils ,以及不受影响的 ex_model2_fixed 都不需要重新生成。

但是这样,我遇到个小问题。如果库文件被修改了,再直接重新编译用到这个库的模型会报错。

>>FixedLib

这时候需要先删除 _sharedutils 文件夹,再重新编译 Top Model

b. 修改库

根据上面的错误提示,如果库模块要改来改去(不建议这么干),不删_sharedutils 倒是也可以,这就需要修改库模块属性,Function name 修改为 myfun_$C 。

打开库文件:>>ssreuselib

打开示例模型

>>ex_topmdl_ssreuse

如上图改库模块属性。如果看到过查表模块的代码,会发现有类似风格的场景。

但是得到的函数原型,添加了一堆标志符checksum,而且 _sharedutils 文件夹下旧版本库模块的 .c.h 文件也不会删掉。

如果库模块发生了变化(不影响计算结果的变化的不算,比如模块位置,大小之类),则这个模块对应的代码必然会重新生成,对应的函数原型(名)也是在变化的

感觉还是不加$C好。

4. 其它

不过除了刚刚这些传统的途径之外,近年版本的 Simulink 还提供了一些新的模块,比如 Simulink Function 模块。

这个以后再说。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3